
'default systems. id is also update order. do not change these.
Const SYSTEM_MOVEMENT:Int = 0
Const SYSTEM_PHYSICS:Int = 1
Const SYSTEM_CAMERA:Int = 2			'game camera
Const SYSTEM_COLLISION:Int = 3
Const SYSTEM_SCRIPT:Int = 4			'lua script engine. BEFORE quadtree
Const SYSTEM_QUADTREE:Int = 5		'quad tree space system
Const SYSTEM_EXPIRATION:Int = 6		'timers for events, etc
Const SYSTEM_RENDER:Int = 0			'there is only one render system


rem
bbdoc: State based game using Artemax systems.
about: Uses one world for all states.
Uses the default systems. This can be changed by overriding the method SetSystems()
end rem
Type TGame Abstract
	
	Field _world:TGame2DWorld
	Field _systemManager:TSystemManager

	'default systems go here.
	Field _updateSystems:TBag

	Field _renderSystem:TRenderSystem
	

	Field _engine:TEngine
	Field _title:String
	
	
	'game resolution. not physical resolution
	Field _gameWidth:Int
	Field _gameHeight:Int
		
	'Collection of game states, by index.
	Field _gameStates:TBag
	
	'The active game state.
	Field _currentGameState:TGameState
		
	'The state we're going to.
	Field _nextGameState:TGameState
	
	'The transition to use when leaving current state.
	Field _leaveTransition:TTransition

	'The transition to use when entering the next state.
	Field _enterTransition:TTransition
		
	'bool. true when virtual resolution is enabled.
	Field _virtualResolution:Int
	
	'sound channels
	Field _soundChannels:TChannel[]
	
	
	Method New()
        _gameStates = New TBag
		_updateSystems = New TBag
		'_renderSystems = New TBag
        Initialize()
    End Method
    
    
    Method Initialize()
		_world = New TGame2DWorld
		_systemManager = _world.GetSystemManager()

		_soundChannels = New TChannel[32]
		For Local index:Int = 0 To _soundChannels.Length - 1
			_soundChannels[index] = AllocChannel()
		Next
		
		SetSystems()
		_systemManager.InitializeAll()
    End Method
	
	
	
	Method ReadWorld:Int(fullPath:String)
		Local s:TStream = ReadStream(fullPath)
		If Not s Then RuntimeError("cannot open world: " + fullPath)
	
		Local rootFolder:String = ExtractDir(fullPath) + "/"
		Local newLine:String
		While Not s.Eof()
			newLine = ReadLine(s)
			Select newLine
				Case "[settings]"
					_world.ReadWorldSettings(s)
				Case "[templatesets]"
					_world.ReadTemplateSets(s, rootFolder)
				Case "[layercontent]"
					GetRenderSystem().Read(s)
				
			End Select
		Wend
		CloseStream(s)
		Return True
	End Method		
	
	
	
	Method WriteWorld(s:TStream)
		_world.WriteWorldSettings(s)
		_world.WriteTemplateSets(s)
		GetRenderSystem().WriteSettings(s)

		'write out entities, per render layer
		Local e:TEntity
		Local entities:TBag = GetRenderSystem().GetActives()
		Local layers:TBag = GetRenderSystem().GetLayers()
		Local different:Int
		
		For Local index:Int = 0 To layers.GetSize() - 1
			Local layer:TBag = TBag(layers.Get(index))
			If layer
				WriteLine(s, "[layercontent]")
				WriteLine(s, "id=" + index)
				For Local entityIndex:Int = 0 To layer.GetSize() - 1
				
					'renderable at this index?
					If layer.Get(entityIndex)
						e = TEntity(entities.Get(entityIndex))
						
						WriteLine(s, "[object]")

						'get template and set id
						'this is stored in the template component
						Local templateComponent:TTemplateComponent = TTemplateComponent(e.GetComponent2(New TTemplateComponent))
						WriteLine(s, "template=" + templateComponent._templateSetID + "," + ..
													templateComponent._templateID)
						
						'only write out the component settings that differ from the
						'settings in the template.
						
						'find the template used to create this entity
						'walk through template settings
						'get the component for this in the entity
						'compare values, write out when different
						Local templateSet:TEntityTemplateSet = TEntityTemplateSet(_world.GetTemplateSets().Get(templateComponent._templateSetID))
						Local template:TEntityTemplate = TEntityTemplate(templateSet.Get(templateComponent._templateID))
						
						'position, not present in template.
						Local positionComponent:TPositionComponent = TPositionComponent(e.GetComponent2(New TPositionComponent))
						WriteLine(s, "position=" + positionComponent.GetX() + "," + positionComponent.GetY())

						'color. auto added when not in template
						different = False
						Local colorComponent:TColorComponent = TColorComponent(e.GetComponent2(New TColorComponent))
						Local array:String = template.GetComponentSetting("color")
						If array <> ""  'in template?
							Local vals:String[] = array.split(",")
							If colorComponent.GetRed() <> Int(vals[0]) Then different = True
							If colorComponent.GetGreen() <> Int(vals[1]) Then different = True
							If colorComponent.GetBlue() <> Int(vals[2]) Then different = True
							If colorComponent.GetAlpha() <> Float(vals[3]) Then different = True
						Else
							different = True
						EndIf
						If different
							WriteLine(s, "color=" + colorComponent.GetRed() + "," + ..
								colorComponent.GetGreen() + "," + colorComponent.GetBlue() + "," + colorComponent.GetAlpha())
						End If
						
						'physics
						different = False
						
						'hitbox
						different = False
						
						'renderform
						different = False
						
						'script
						different = False
						
						'transform						
						different = False
						
						
											
						WriteLine(s, "[/object]")
					End If
				Next
				WriteLine(s, "[/layercontent]")
			End If
		Next
	End Method
	
    
		
	rem
	bbdoc: Plays a previously loaded sound on passed channel or random channel if none provided.
	returns: Used channel.
	endrem
	Method PlaySound:TChannel(s:TSound, c:TChannel, volume:Float)	
		If c = Null
			'find a free channel
			For Local index:Int = 0 To _soundChannels.Length - 1
				Local channel:TChannel = _soundChannels[index]
				If Not channel.Playing()
					c = channel
					Exit
				EndIf
			Next
			
			'overload. grab first channel. =/
			If c = Null Then c = _soundChannels[0]
		EndIf
		
		'play sound
		c.SetVolume(0.0)
		CueSound(s, c)
		c.SetVolume(volume)
		ResumeChannel(c)
		Return c
	End Method
	
	
	rem
	bbdoc: Stops all sound channels.
	endrem
	Method StopAllSound()
		For Local index:Int = 0 To _soundChannels.Length - 1
			Local channel:TChannel = _soundChannels[index]
			If channel.Playing() Then channel.Stop()
		Next
	End Method
	
	
	rem
	bbdoc: Sets game name.
	endrem
	Method SetTitle(newName:String)
		_title = newName
		AppTitle = newName
	End Method
	
	
	rem
	bbdoc: Returns game name.
	endrem
	Method GetTitle:String()
		Return _title
	End Method	
	
	
	Method SetEngine(e:TEngine)
		_engine = e
	End Method
	
	
	rem
	bbdoc: Asks the engine to stop running.
	endrem
	Method RequestStop()	
		_engine.RequestStop()
	End Method
	
	
	rem
	bbdoc: Sets pause mode value.
	endrem
	Method SetPause(bool:Int)
		_engine.SetPaused(bool)
	End Method
	

	rem
	bbdoc: Returns the pause mode value.
	endrem	
	Method IsPaused:Int()
		Return _engine.IsPaused()
	End Method
	
	
	rem
	bbdoc: Sets game resolution.
	endrem
	Method SetGameResolution(w:Int, h:Int)
		_virtualResolution = True
		_gameWidth = w
		_gameHeight = h
		SetVirtualResolution(w, h)
	End Method
	
	
	rem
	bbdoc: Returns game width, or physical width if no virtual resolution has been used.
	endrem
	Method GetWidth:Int()
		If _virtualResolution Then Return _gameWidth
		Return GraphicsWidth()
	End Method
	

	rem
	bbdoc: Returns game height, or physical height if no virtual resolution has been used.
	endrem	
	Method GetHeight:Int()
		If _virtualResolution Then Return _gameHeight
		Return GraphicsHeight()
	End Method
	


	Method Update(delta:Double)
		_world.LoopStart()
		_world.SetDelta(delta)		

		If _leaveTransition
			_leaveTransition.Update(delta)
			If _leaveTransition.IsComplete()
				_currentGameState.Leave()
				_currentGameState = _nextGameState
				_nextGameState = Null
				_leaveTransition = Null
				_currentGameState.Enter()
				If _enterTransition Then _enterTransition.Initialize()
			Else
				Return
			End If
		End If
		If _enterTransition
			_enterTransition.Update(delta)
			If _enterTransition.IsComplete()
				_enterTransition = Null
			Else
				Return
			End If
		End If
				
		_currentGameState.PreUpdate(delta)
		
		'update systems.
		For Local i:Int = 0 To _updateSystems.GetSize() - 1
			If _updateSystems.Get(i)
				TEntitySystem(_updateSystems.Get(i)).Process()
			EndIf
		Next
		
		'update current state.
		_currentGameState.Update(delta)
		_currentGameState.PostUpdate(delta)
		
		'F5 refreshes all entity scripts
		If GetScriptSystem()
			If KeyHit(KEY_F5) Then GetScriptSystem().RefreshAllScripts()
		EndIf
		
	End Method
	
	
	
	Method Render(tween:Double)
		_world.SetTween(tween)
		_currentGameState.PreRender(tween)
		_renderSystem.Process()
		_currentGameState.Render(tween)
		
		If _leaveTransition
			_leaveTransition.Render(Self)
		ElseIf _enterTransition
			_enterTransition.Render(Self)
		EndIf
		
		_currentGameState.PostRender(tween)
		
		'display possible LUA errors.
		If GetScriptSystem()
			TRenderstate.Push()
			TRenderstate.Reset()
			SetColor(255, 0, 0)
			SetScale(0.7, 0.7)
			DrawText(GetScriptSystem().GetCurrentError(), 0, GetHeight() - 11)
			TRenderState.Pop()
		End If
	End Method
	
	
	
	Rem
	bbdoc: Enter a gamestate with the provided transitions.
	endrem
	Method EnterState(id:Int, enter:TTransition = Null, leave:TTransition = Null)
		If Not enter Then enter = New TTransitionEmpty
		If Not leave Then leave = New TTransitionEmpty
		_enterTransition = enter
		_leaveTransition = leave
		
		_nextGameState = GetGameStateByID(id)
		If Not _nextGameState Then RuntimeError "Cannot find state with id:" + id
	
		_leaveTransition.Initialize()
	End Method
			
	

	rem
	bbdoc: Adds state to this game with specified id.
	about: First state added is the default state.
	endrem
	Method AddGameState(gameState:TGameState, id:Int)
		_gameStates.Set(id, gameState)
		gameState.SetId(id)
		gameState.SetGame(Self)
		gameState.SetWorld(_world)
		If Not _currentGameState Then _currentGameState = gameState
	End Method
	
	
	'Sets engine default systems.
	'This method can be overridden if you want total control over the systems.
	Method SetSystems()
	
		'add update systems		
		AddUpdateSystem(_systemManager.SetSystem(New TCameraSystem), SYSTEM_CAMERA)
		AddUpdateSystem(_systemManager.SetSystem(New TQuadTreeSystem), SYSTEM_QUADTREE)
		AddUpdateSystem(_systemManager.SetSystem(New TMovementSystem), SYSTEM_MOVEMENT)
		AddUpdateSystem(_systemManager.SetSystem(New TCollisionSystem), SYSTEM_COLLISION)
		AddUpdateSystem(_systemManager.SetSystem(New TPhysicsSystem), SYSTEM_PHYSICS)
		AddUpdateSystem(_systemManager.SetSystem(New TScriptSystem), SYSTEM_SCRIPT)
		
		'set references to this game in systems
		Local camera:TCameraSystem = GetCameraSystem()
		camera.SetGame(Self)
		Local script:TScriptSystem = Getscriptsystem()
		script.SetGame(Self)
		'make collision system aware of the quad tree system
		Local collision:TCollisionSystem = GetCollisionSystem()
		collision.SetQuadTree(GetQuadTreeSystem())
		
		'add render system
		_renderSystem  = New TRenderSystem
		_renderSystem.SetQuadTree(Self.GetQuadTreeSystem())
		_renderSystem.SetCamera(Self.GetCameraSystem())
		_systemManager.SetSystem(_renderSystem)

	End Method
		
	
	
	rem
	bbdoc: Renders text, centered and with shadow when specified.
	endrem
	Method RenderText(text:String, xpos:Int, ypos:Int, centered:Int = False, shadow:Int = False, shadowDistance:Float = 1.0)
	
		Local xscale:Float
		Local yscale:Float
		GetScale(xscale, yscale)
		
		If centered Then xpos = GetWidth() / 2 - ((TextWidth(text) * xscale) / 2)
		If shadow
			Local r:Int, g:Int, b:Int
			GetColor(r, g, b)
			SetColor 0, 0, 0
			DrawText(text, xpos, ypos + shadowDistance * yscale)
			SetColor (r, g, b)
		End If
		DrawText(text, xpos, ypos)
	End Method
	
	
	Method GetMovementSystem:TMovementSystem()
		Return TMovementSystem(GetUpdateSystem(SYSTEM_MOVEMENT))
	End Method
	
	Method GetCameraSystem:TCameraSystem()
		Return TCameraSystem(GetUpdateSystem(SYSTEM_CAMERA))
	End Method

	Method GetQuadTreeSystem:TQuadTreeSystem()
		Return TQuadTreeSystem(GetUpdateSystem(SYSTEM_QUADTREE))
	End Method
	
	Method GetCollisionSystem:TCollisionSystem()
		Return TCollisionSystem(GetUpdateSystem(SYSTEM_COLLISION))
	End Method
	
	Method GetPhysicsSystem:TPhysicsSystem()
		Return TPhysicsSystem(GetUpdateSystem(SYSTEM_PHYSICS))
	End Method
	
	Method GetScriptSystem:TScriptSystem()
		Return TScriptSystem(GetUpdateSystem(SYSTEM_SCRIPT))
	End Method
		
	Method GetExpirationSystem:TExpirationSystem()
		Return TExpirationSystem(GetUpdateSystem(SYSTEM_EXPIRATION))
	End Method
	
	

	rem
	bbdoc: Adds an update system to this game, by index.
	endrem
	Method AddUpdateSystem(system:TEntitySystem, index:Int)
		_updateSystems.Set(index, system)
	End Method	
	
		
	rem
	bbdoc: Returns update system by index.
	endrem
	Method GetUpdateSystem:TEntitySystem(index:Int)
		Return TEntitySystem(_updateSystems.Get(index))
	End Method
	
	
	rem
	bbdoc: Returns render system by index.
	endrem
	Method GetRenderSystem:TRenderSystem()
		Return _renderSystem
	End Method

		
	rem
	bbdoc: Returns true when game is transitioning between states.
	endrem	
	Method Transitioning:Int()
		Return _enterTransition Or _leaveTransition <> Null
	End Method
	
		
	Rem
	bbdoc: Returns the current game state.
	endrem
	Method GetCurrentState:TGameState()
		Return _currentGameState
	End Method
		
	
	Rem
	bbdoc: Returns the ID of the current game state.
	endrem	
	Method GetCurrentStateID:Int()
		Return _currentGameState.GetId()
	End Method	
	
	
	Rem
	bbdoc: Returns game state by specified ID.
	endrem
	Method GetGameStateByID:TGameState(id:Int)
		Return TGameState(_gameStates.Get(id))
	End Method	
	
	
	rem
	bbdoc: User hook to perform game startup logic.
	about: This method is called when the engine starts.
	endrem
	Method Startup()
	End Method
	

	'This method is called when the engine stops.
	Method CleanUp()
		StopAllSound()
		_systemManager = Null
		_updateSystems.Clear()
		_updateSystems = Null
		_renderSystem = Null
		_world = Null
	End Method
	
End Type

